package net.java.mediasystems.web.bean;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.validator.ValidatorException;

import org.primefaces.context.RequestContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import net.java.mediasystems.database.core.model.Metadata;
import net.java.mediasystems.database.core.model.MetadataValue;
import net.java.mediasystems.database.core.service.MetadataService;
import net.java.mediasystems.web.WebUtil;
import net.java.mediasystems.web.converter.MetadataConverter;

public abstract class AbstractEmbeddedMetadataValueInputBean2 implements Serializable {

	private static final long serialVersionUID = 8752642913868360859L;
	
	private static final Logger LOG = LoggerFactory.getLogger(AbstractEmbeddedMetadataValueInputBean2.class);
	
	private String metadataDefName;	// For Input of new Metadata-Definition
	private List<Metadata> availableMetadataDefinitions;
	private Metadata selectedMetaDataDef;	// Part of the Input of a new Metadata-Value
	private String metadataValue;			// Part of the Input of a new Metadata-Value
	
	private List<MetadataValue> addedMetaDataValues = new ArrayList<MetadataValue>();
	
	private transient MetadataService metadataService;
	
	private MetadataConverter metadataConverter;
	
	abstract String getMetadataCategory();
	
	/**
	 * Adds a Metadata-Value to the MetadataValue-List.
	 */
	public void addMetadataValue() {
		LOG.info(String.format("Creating MetadataValue with value \"%s\" and adding it to the MetadataValuesList", this.metadataValue));
		
		MetadataValue metadataValue = new MetadataValue();
		metadataValue.setMetadata(this.selectedMetaDataDef);
		metadataValue.setValue(this.metadataValue);
		
		this.addedMetaDataValues.add(metadataValue);
		
		resetMetadataValueInput();
	}
	
	private void resetMetadataValueInput() {
		this.metadataValue = null;
	}
	
	/**
	 * Save the Metadata, which can afterwards be bound to Object, that can hold Metadata (a StorageContainer or a MediaItem).
	 */
	public void saveMetadataDefinition() {
		System.out.println("Calling the method saveMetadataDefinition()");
		
		Metadata metadata = new Metadata();
		metadata.setName(this.metadataDefName);
		metadata.setCategory(getMetadataCategory());
		
		this.metadataService.addMetadata(metadata);
		
		this.availableMetadataDefinitions.add(metadata);
		sortMetadataDefinitionList();
		
		this.metadataConverter.addNewMetadataDefinition(metadata);
		
		resetMetadataDefinitionInput();
		
		// Use RequestContext to execute Javascript to close the modal Dialog
		RequestContext.getCurrentInstance().execute("mdefInputDialog.hide();");
	}
	
	private void resetMetadataDefinitionInput() {
		this.metadataDefName = null;
	}
	
	/**
	 * Validation-Method to check, if the Name already exists for the defined Category.
	 * 
	 * @param facesContext FacesContext
	 * @param uiComponent UIComponent
	 * @param value Object
	 */
	public void checkNameForCategory(FacesContext facesContext, UIComponent uiComponent, Object value) {
		String name = (String) value; 
		System.out.println("Checking the Name " + name + ", if it exists already in the DB!!!");
		if (this.metadataService.getMetadataDefinitionByCategoryAndName(getMetadataCategory(), name) != null) {
			throw new ValidatorException(WebUtil.createErrorMessageLocalized("inc_addmetadatadefinitionmodaldialog_error_namealreadyexists"));
		}
	}
	
	public List<Metadata> getAvailableMetadataDefinitions() {
		if (this.availableMetadataDefinitions == null) {
			this.availableMetadataDefinitions = this.metadataService.getAllMetadataDefinitionByCategory(getMetadataCategory());
			sortMetadataDefinitionList();
		}
		return availableMetadataDefinitions;
	}
	
	private void sortMetadataDefinitionList() {
		Collections.sort(this.availableMetadataDefinitions, new MetadataDefinitionComparator());
	}
	
	public void setAvailableMetadataDefinitions(
			List<Metadata> availableMetadataDefinitions) {
		this.availableMetadataDefinitions = availableMetadataDefinitions;
	}
	
	public String getMetadataDefName() {
		return metadataDefName;
	}
	
	public void setMetadataDefName(String metadataDefName) {
		this.metadataDefName = metadataDefName;
	}
	
	public String getMetadataValue() {
		return metadataValue;
	}
	
	public void setMetadataValue(String metadataValue) {
		this.metadataValue = metadataValue;
	}
	
	public void setMetadataService(MetadataService metadataService) {
		this.metadataService = metadataService;
	}
	
	public Metadata getSelectedMetaDataDef() {
		return selectedMetaDataDef;
	}
	
	public void setSelectedMetaDataDef(Metadata selectedMetaDataDef) {
		LOG.info("Setting MetadataDef to " + selectedMetaDataDef);
		this.selectedMetaDataDef = selectedMetaDataDef;
	}
	
	public MetadataConverter getMetadataConverter() {
		return metadataConverter;
	}
	
	public void setMetadataConverter(MetadataConverter metadataConverter) {
		this.metadataConverter = metadataConverter;
	}
	
	public List<MetadataValue> getAddedMetaDataValues() {
		return addedMetaDataValues;
	}
	
	public void setAddedMetaDataValues(List<MetadataValue> addedMetaDataValues) {
		this.addedMetaDataValues = addedMetaDataValues;
	}
	
	class MetadataDefinitionComparator implements Comparator<Metadata> {

		@Override
		public int compare(Metadata o1, Metadata o2) {
			return o1.getName().compareTo(o2.getName());
		}
	}
}
